package edu.chalmers.AV_2DPlatformerFramework.Utility;

import edu.chalmers.AV_2DPlatformerFramework.Model.Texture;
import java.awt.Polygon;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JComponent;

/**
 * Various static utility methods which may or may not be of any use to you.
 * @version 0.2
 * @author Richard
 */
public abstract class Utility {
    
    //Linear operations

    /**
     * Changes the value in the Polygon's x and y arrays.
     * @param p The polygon to translate.
     * @param dX Delta X.
     * @param dY Delta Y.
     @deprecated Due to bounding box not following, should use {@link java.awt.Polygon.translate()} instead.
     */
    //How do i get the ****ing link not to add "Utility"?!
    @Deprecated
    public static void translate(Polygon p, int dX, int dY){
        uniformAddition(p.xpoints, dX);
        uniformAddition(p.ypoints, dY);
    }
    public static void scalar(Polygon p, double scalar){
        uniformMultiplication(p.xpoints, scalar);
        uniformMultiplication(p.ypoints, scalar);
    }
    /**
     * Updated to no longer use the reflection method, which suffers from rounding issues.
     * @param p The polygon to flip.
     */
    //Also changed so that the Polygon translate method is used, though this
    //isn't neccesary since the bounding box doesnt have to be translated in this case.
    public static void flipY(Polygon p){
        //Calculate the distance to move the shape so that the line to rotate around passes through the origin.
        int y_Offset = LineDistanceFromZero(p.ypoints)+findGreatestDifference(p.ypoints)/2;
        p.translate(0, -y_Offset);
        uniformMultiplication(p.ypoints, -1);
        p.translate(0, y_Offset);
    }
    /**
     * Reflects a Polygon p in the line through origin with the vector given by (x, y).
     * WARNING: May reduce the total size of the Polygon due to rounding issues!
     * Note that the line will always pass through the origin, and that the vector's
     * length must  be equal or greater to the polygon, or the polygon will be disfigured.
     * @param p The Polygon to reflect.
     * @param x The x-component of the line vector.
     * @param y The y-component of the line vector.
     */
    public static void reflection(Polygon p, double x, double y){
        double divisor = x*x+y*y;
        for (int i = 0; i<p.npoints; i++){
            double pX = p.xpoints[i];
            double pY = p.ypoints[i];
            double scalar = (pX*x + pY*y)/divisor;
            
//            System.out.println("BEFORE: (" + p.xpoints[i] + ", " + p.ypoints[i] + ")");

            p.xpoints[i] = roundToInteger(2*scalar*x - pX);
            p.ypoints[i] = roundToInteger(2*scalar*y - pY);
            
//            System.out.println("AFTER: (" + p.xpoints[i] + ", " + p.ypoints[i] + ")");
        }       
    }
         
            
    //Basics
    public static int findGreatestDifference(int[] array){
        int min = array[0];
        int max = array[0];
        for (int i = 1; i<array.length; i++){
            if (array[i] < min){
                min = array[i];
            } else if (array[i] > max){
                max = array[i];
            }
        }
        return max-min;
    }     
    public static void uniformDivision(int[] array, double divisor){
        for (int i = 0; i<array.length; i++){
            array[i] = (int)Math.round(array[i]/divisor);
        }
    }
    public static void uniformMultiplication(int[] array, double multiplier){
        for (int i = 0; i<array.length; i++){
            array[i] = (int)Math.round(array[i] * multiplier);
        }
    }    
    public static void uniformAddition(int[] array, int addend){
        for (int i = 0; i<array.length; i++){
            array[i] = array[i] + addend;
        }
    }
    public static void uniformSubtraction(int[] array, int subtrahend){
        for (int i = 0; i<array.length; i++){
            array[i] = array[i] + subtrahend;
        }
    }
    public static void uniformModulus(int[] array, int modulo){
        for (int i = 0; i<array.length; i++){
            array[i] = array[i]%modulo;
        }
    }
    public static void uniformSwapSign(int[] array){
        for (int i = 0; i<array.length; i++){
            array[i] = -array[i];
        }        
    }
    /**
     * Returns the smallest distance from zero for a series of merged lines.
     * Note that 0 is returned if any line crosses through the origin.
     * @param array The array to test.
     * @return Minimum distance from 0.
     */
    public static int LineDistanceFromZero(int[] array){
        int minDistance = Math.abs(array[0]);
        int firstSign = array[0]/array[0]; //+-1
        for (int i = 1; i<array.length; i++){
            if (array[i] == 0){
                return 0;
            } else if (firstSign*array[i] < 0 ){
                return 0;
            } else {
                minDistance = Math.abs(array[i]) < minDistance ? Math.abs(array[i]) : minDistance;
            }
        } 
        return minDistance;
    }
    
    public static int roundToInteger(double arg){
        return (int)(arg+0.5);
    }
    
    public static String getPolygonString(Polygon p){
        String line1 = "X: [";
        String line2 = "Y: [";
        final int n = p.npoints;
        for (int i = 0; i < n; i++){
            line1 = line1 + p.xpoints[i] + ", ";
            line2 = line2 + p.ypoints[i] + ", ";
        }
        line1 = line1 + "]";
        line2 = line2 + "]";
        return line1 + "\n" + line2;
    }
    public static void printPolygon(Polygon p){
        System.out.println(getPolygonString(p));
    }
    /**
     * @param p The polygon to check.
     * @param component The area in which the polygon should lay.
     * @return True if every pixel of the polygon is visible on the given JComponent.
     */
    public static boolean polygonFullyVisible(Polygon p, JComponent component){
        return (p.getBounds2D().getX() < 0 || p.getBounds2D().getY() < 0
            || component.getWidth() - (p.getBounds2D().getX()+p.getBounds2D().getWidth()) < 0
            || component.getHeight() - (p.getBounds2D().getY()+p.getBounds2D().getHeight()) < 0 );
    }

    
    /**
     * Provides an easy way of generating a BufferedImage from an url.
     * @param imageURL The URL of the image to use as texture.
     * @return A buffered image created from the file at the given URL, or null if no image could be found.
     */
    public static BufferedImage generateBufferedImage(String imageURL){
            File img = new File(imageURL);
        try {
            return ImageIO.read(img);
        } catch (IOException ex) {
            System.out.println("Failed to create image from file: " + imageURL);
            return null;
        }
    }
    /**
     * Construct a Texture[] array from all the files on a folder.
     * Use to easily create an AnimatedTexture.
     * @param folderURL The URL of the folder.
     * @param anchor The anchor of the textures.
     * @return An array of Textures.
     */
    public static Texture[] buildTextureArray(String folderURL, Rectangle2D anchor){
        File folder = new File(folderURL);
        File[] fileArray = folder.listFiles();
        
        ArrayList<Texture> tempList = new ArrayList();
        
        for (File item : fileArray) {
            if (item.isFile()) {

                tempList.add(new Texture(item.getAbsolutePath(), anchor));
            } else if (item.isDirectory()) {
                System.out.println(item.getName() + " is a directory, ignored.");
            }
        }       
        Object[] tmp = tempList.toArray();
        Texture[] ans = new Texture[tmp.length];
        
        for(int i = 0; i < tmp.length; i++){
            if (tmp[i] instanceof Texture){
                ans[i] = (Texture) tmp[i];
            }
        }
        return ans;
    }
}
